package cn.lili.modules.system.aspect.interceptor;

import cn.lili.modules.system.aspect.annotation.SystemLogPoint;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.UserEnums;
import cn.lili.common.utils.IpHelper;
import cn.lili.common.utils.SpelUtil;
import cn.lili.common.utils.ThreadPoolUtil;
import cn.lili.modules.permission.entity.vo.SystemLogVO;
import cn.lili.common.utils.IpUtils;
import cn.lili.modules.permission.service.SystemLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Spring AOP实现日志管理
 *
 * @author Chopper
 */
@Aspect
@Component
@Slf4j
public class SystemLogAspect {

	/**
	 * 启动线程异步记录日志
	 */
	private static final ThreadLocal<Date> BEGIN_TIME_THREAD_LOCAL = new NamedThreadLocal<>("SYSTEM-LOG");

	@Autowired
	private SystemLogService systemLogService;

	@Autowired
	private HttpServletRequest request;

	@Autowired
	private IpHelper ipHelper;

	/**
	 * Controller层切点,注解方式
	 */
	@Pointcut("@annotation(cn.lili.modules.system.aspect.annotation.SystemLogPoint)")
	public void controllerAspect() {

	}

	/**
	 * 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间
	 */
	@Before("controllerAspect()")
	public void doBefore() {
		BEGIN_TIME_THREAD_LOCAL.set(new Date());
	}

	/**
	 * 后置通知(在方法执行之后并返回数据) 用于拦截Controller层无异常的操作
	 *
	 * @param joinPoint
	 *            切点
	 */
	@AfterReturning(returning = "rvt", pointcut = "controllerAspect()")
	public void after(JoinPoint joinPoint, Object rvt) {
		try {
			Map map = spelFormat(joinPoint, rvt);
			String description = map.get("description").toString();
			String customerLog = map.get("customerLog").toString();
			String type = map.get("type").toString();

			Map<String, String[]> logParams = request.getParameterMap();
			AuthUser authUser = UserContext.getCurrentUser();
			SystemLogVO systemLogVO = new SystemLogVO();

			if (authUser == null) {
				// 如果是商家则记录商家id，否则记录-1，代表平台id
				systemLogVO.setStoreId(-2L);
				// 请求用户
				systemLogVO.setUsername("游客");
			} else {
				// 如果是商家则记录商家id，否则记录-1，代表平台id
				systemLogVO.setStoreId(
						authUser.getRole().equals(UserEnums.STORE) ? Long.parseLong(authUser.getStoreId()) : -1);
				// 请求用户
				systemLogVO.setUsername(authUser.getUsername());
			}

			// 日志标题
			systemLogVO.setName(description);
			// 日志请求url
			systemLogVO.setRequestUrl(request.getRequestURI());
			// 请求方式
			systemLogVO.setRequestType(request.getMethod());
			// 请求参数
			systemLogVO.setMapToParams(logParams);
			// 响应参数 此处数据太大了，所以先注释掉
			// systemLogVO.setResponseBody(JSONUtil.toJsonStr(rvt));
			// 请求IP
			systemLogVO.setIp(IpUtils.getIpAddress(request));
			// IP地址
			systemLogVO.setIpInfo(ipHelper.getIpCity(request));
			// 写入自定义日志内容
			systemLogVO.setCustomerLog(customerLog);
			systemLogVO.setType(type);
			// 请求开始时间
			long beginTime = BEGIN_TIME_THREAD_LOCAL.get().getTime();
			long endTime = System.currentTimeMillis();
			// 请求耗时
			Long usedTime = endTime - beginTime;
			systemLogVO.setCostTime(usedTime.intValue());
			// 调用线程保存
			ThreadPoolUtil.getPool().execute(new SaveSystemLogThread(systemLogVO, systemLogService));

			BEGIN_TIME_THREAD_LOCAL.remove();
		} catch (Exception e) {
			log.error("系统日志保存异常", e);
		}
	}

	/**
	 * 保存日志
	 */
	private static class SaveSystemLogThread implements Runnable {
		@Autowired
		private SystemLogVO systemLogVO;
		@Autowired
		private SystemLogService systemLogService;

		public SaveSystemLogThread(SystemLogVO systemLogVO, SystemLogService systemLogService) {
			this.systemLogVO = systemLogVO;
			this.systemLogService = systemLogService;
		}

		@Override
		public void run() {
			try {
				systemLogService.saveLog(systemLogVO);
			} catch (Exception e) {
				log.error("系统日志保存异常,内容{}：", systemLogVO, e);
			}
		}
	}

	/**
	 * 获取注解中对方法的描述信息 用于Controller层注解
	 *
	 * @param joinPoint
	 *            切点
	 * @return 方法描述
	 * @throws Exception
	 */
	private static Map<String, String> spelFormat(JoinPoint joinPoint, Object rvt) {

		Map<String, String> result = new HashMap<>(2);
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		SystemLogPoint systemLogPoint = signature.getMethod().getAnnotation(SystemLogPoint.class);
		String description = systemLogPoint.description();
		String type = systemLogPoint.type();
		String customerLog = "";
		if (systemLogPoint.parameter()) {
			customerLog = SpelUtil.compileParams(joinPoint, rvt, systemLogPoint.customerLog());
		} else {
			customerLog = systemLogPoint.customerLog();
		}
		result.put("description", description);
		result.put("customerLog", customerLog);
		result.put("type", type);
		return result;
	}

}
